using System;
using System.Collections.Generic;
using System.ComponentModel;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using FilteredStatistics.Common;
using FilteredStatistics.Controller;
using FilteredStatistics.View;
using System.Diagnostics;

namespace FilteredStatistics.Data
{
    class ElevationNamedZone : INamedZone
    {
        public ElevationNamedZone(IActivity activity, float startElevation, float endElevation, Length.Units elevationUnit)
        {
            m_ActivityCallbackHelper = new ActivityDataChangedHelper(m_Activity);
            m_ActivityCallbackHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

            Init(activity, startElevation, endElevation, elevationUnit);
        }

        void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
        }

#region INamedZone members

        public String Name
        {
            get
            {
                String lapName = String.Format("[{0:0.00}; {1:0.00}]{2}", m_StartElevation, m_EndElevation, Length.LabelAbbr(m_ElevationUnit));

                return lapName;
            }
        }

        public IValueRangeSeries<DateTime> ValidTimes
        {
            get
            {
                RefreshValidTimes();

                return m_ValidTimes;
            }
        }

        public event PropertyChangedEventHandler ValidTimesChanged;

#endregion

        protected void TriggerValidTimesChanged()
        {
            if (ValidTimesChanged != null)
            {
                ValidTimesChanged(this, new PropertyChangedEventArgs("ValidTimes"));
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            ElevationNamedZone namedZone = obj as ElevationNamedZone;

            if (namedZone != null)
            {
                // Diff distance with an epsilon of 0.001 to prevent rounding errors
                float startDiff = namedZone.m_StartElevation - m_StartElevation;
                float endDiff = namedZone.m_EndElevation - m_EndElevation;

                return Math.Abs(startDiff) < 0.001f &&
                       Math.Abs(endDiff) < 0.001f &&
                       namedZone.m_ElevationUnit == m_ElevationUnit;
            }

            return base.Equals(obj);
        }

        private void RefreshValidTimes()
        {
            if (m_ValidTimesDirty)
            {
                m_ValidTimesDirty = false;
                m_ValidTimes.Clear();

                if (Activity != null)
                {
                    ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(Activity);
                    Debug.Assert(activityInfo != null);
                    INumericTimeDataSeries dataTrack = activityInfo.SmoothedElevationTrack;

                    if(dataTrack != null && dataTrack.Count > 0)
                    {
                        // Since we cannot create a ZoneCategory directly, we must create a temp one and make
                        //  sure we remove it from the list.  We chose to use the climb zones as the template
                        //  but it's irrelevant
                        IZoneCategory tempElevationZone = PluginMain.GetApplication().Logbook.ClimbZones.Add();
                        ZoneCategoryInfo categoryInfo;

                        // Remove from the list, we don't want the user to be affected.
                        PluginMain.GetApplication().Logbook.ClimbZones.Remove(tempElevationZone);

                        // Setup temp zone category & calculate
                        tempElevationZone.Zones.Clear();
                        tempElevationZone.Zones.Add(new NamedLowHighZone("Elevation",
                                                                        (float)Length.Convert(m_StartElevation, m_ElevationUnit, Length.Units.Meter),
                                                                        (float)Length.Convert(m_EndElevation, m_ElevationUnit, Length.Units.Meter)));
                        categoryInfo = ZoneCategoryInfo.Calculate(activityInfo, tempElevationZone, dataTrack, 1.0f);

                        m_ValidTimes = categoryInfo.Zones[0].Times;
                    }
                }

                TriggerValidTimesChanged();
            }
        }

        public void Init(IActivity activity, float startElevation, float endElevation, Length.Units elevationUnit)
        {
            m_StartElevation = startElevation;
            m_EndElevation = endElevation;
            m_ElevationUnit = elevationUnit;

            Activity = activity;
        }

        public IActivity Activity
        {
            get { return m_Activity; }
            set
            {
                if (Activity != value)
                {
                    m_Activity = value;
                    m_ActivityCallbackHelper.Activity = m_Activity;

                    m_ValidTimesDirty = true;
                    TriggerValidTimesChanged();
                }
            }
        }

        private IActivity m_Activity;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private float m_StartElevation = 0;
        private float m_EndElevation = 0;
        private Length.Units m_ElevationUnit = Length.Units.Kilometer;
        IValueRangeSeries<DateTime> m_ValidTimes = new ValueRangeSeries<DateTime>();
        private bool m_ValidTimesDirty = false;
    }
}
